package br.com.intelsys.iflow.jbpm;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jbpm.JbpmContext;
import org.jbpm.JbpmException;
import org.jbpm.graph.def.Transition;
import org.jbpm.graph.exe.ProcessInstance;
import org.jbpm.graph.exe.Token;

import br.com.intelsys.iflow.IFlowException;
import br.com.intelsys.iflow.IFlowExceptionTransitionNotFound;
import br.com.intelsys.iflow.IFlowInstance;
import br.com.intelsys.iflow.IFlowTransition;

/**
 * Encapsula uma instancia do processo BPM baseado no jBPM da JBOSS.
 * 
 * @author Patrick Givisiez
 *
 */

class IFlowInstanceJbpm implements IFlowInstance {
	ProcessInstance processInstance;
	private JbpmContext jbpmContext;
	Token token;
	private static final Log log = LogFactory.getLog(IFlowInstanceJbpm.class);
	
	IFlowInstanceJbpm(JbpmContext jbpmContext, ProcessInstance pi) {
		if (pi != null) {
			this.jbpmContext = jbpmContext;
			this.processInstance = pi;
		} else {
			log.error("ATENCAO: O ProcessInstance esta nulo!!!");
		}
	}
	
	private boolean loadToken() {
		if (this.token == null) {
			this.token = this.processInstance.getRootToken();
			log.debug("Recuperando meu Root Token. Sucesso? "+(this.token != null));
		}
		return(this.token != null);
	}
	
	public void signal() throws IFlowException {
		this.signal((String)null);
	}

	public void signal (IFlowTransition transition) throws IFlowException {
		if (transition != null) {
			this.signal(transition.getName());
		} else {
			this.signal((String)null);
		}
	}
	
	public void signal(String transitionName) throws IFlowException {
		if (this.loadToken()) {
			if (transitionName == null) {
				log.debug("Enviando um Token Signal vazio");
				this.token.signal();
			} else {
				log.debug("Enviando um Token Signal: "+transitionName);
				try {
					this.token.signal(transitionName);
				} catch (JbpmException e) {
					String msg = e.getMessage();
					if (msg != null && msg.indexOf("does not exist on") != -1) {
						throw new IFlowExceptionTransitionNotFound(e);
					} else {
						throw new IFlowException(e);
					}
				}
			}
		}
	}
	
	public String getStateName() {
		String name;
		if (this.loadToken()) {
			if (this.token.getNode() != null) {
				name = this.token.getNode().getName();
			} else {
				log.warn("Este token nao possui um node: "+this.token.getId());
				name = null;
			}
		} else {
			name = null;
		}
		return(name);
	}
	
	public void setVariable(String name, Object value) {
		log.debug("Definindo uma variavel no contexto de nome: "+name+" que sera "+value);
		this.processInstance.getContextInstance().setVariable(name, value);
	}
	public Object getVariable(String name) {
		return(this.processInstance.getContextInstance().getVariable(name));
	}
	public Map getVariables() {
		return(this.processInstance.getContextInstance().getVariables());
	}

	public IFlowTransition getTransition(String name) {
		IFlowTransition retTmp = null;
		if (name != null && this.loadToken()) {
			Set <Transition> s = this.token.getAvailableTransitions();
			for (Transition tr : s) {
				if (name.equals(tr.getName())) {
					retTmp = new IFlowTransitionJbpm(tr);
				}
			}
		}
		return(retTmp);
	}

	public List <IFlowTransition> getTransitionList () {
		List <IFlowTransition> tList = new ArrayList <IFlowTransition>();
		if (this.loadToken()) {
			Set <Transition> s = this.token.getAvailableTransitions();
			tList = new ArrayList <IFlowTransition> (s.size());
			for (Transition tr : s) {
				tList.add(new IFlowTransitionJbpm(tr));
			}
		} else {
			tList = new ArrayList <IFlowTransition>(0);
		}
		return(tList);
	}
	
	public long getInstanceId() {
		return(this.processInstance.getId());
	}
	
	public void save() {
		log.debug("Salvando a instancia: "+getInstanceId());
		this.jbpmContext.save(processInstance);
	}
}
